<?php

namespace App\Kuafu\Order\Controller\Pos;

use App\Kuafu\Activity\Enum\ActivityEnum;
use App\Kuafu\Activity\Service\ActivityService;
use App\Kuafu\Base\Controller\BaseController;
use App\Kuafu\Common\Service\CommonService;
use App\Kuafu\Coupon\Enum\CouponEnum;
use App\Kuafu\Coupon\Service\CouponService;
use App\Kuafu\CouponExt\Enum\CouponExtEnum;
use App\Kuafu\CouponExt\Exceptions\CouponExtException;
use App\Kuafu\CouponExt\Model\OrderCouponExtSplitModel;
use App\Kuafu\CustomerCoupon\Exceptions\CustomerCouponException;
use App\Kuafu\CustomerCoupon\Service\CustomerCouponService;
use App\Kuafu\CustomPayment\Service\CustomPaymentService;
use App\Kuafu\Invoice\Service\InvoiceService;
use App\Kuafu\Nsq\Enum\NsqCodeEnum;
use App\Kuafu\Nsq\Jobs\NsqSendJob;
use App\Kuafu\Nsq\Service\NsqService;
use App\Kuafu\Order\Enum\CodeEnum;
use App\Kuafu\Order\Enum\CodePaymentEnum;
use App\Kuafu\Order\Enum\OrderCacheEnum;
use App\Kuafu\Order\Enum\OrderCodeEnum;
use App\Kuafu\Order\Enum\OrderInfoEnum;
use App\Kuafu\Order\Exceptions\OrderException;
use App\Kuafu\Order\Exceptions\OrderWaringException;
use App\Kuafu\Order\Jobs\OrderPayPolling;
use App\Kuafu\Order\Model\OrderExternalSplitModel;
use App\Kuafu\Order\Service\OrderCouponExternalService;
use App\Kuafu\Order\Service\OrderHandleService;
use App\Kuafu\Order\Service\OrderInvoiceService;
use App\Kuafu\Order\Service\OrderLockService;
use App\Kuafu\Order\Service\OrderOfflineService;
use App\Kuafu\Order\Service\OrderPay;
use App\Kuafu\Order\Service\OrderRefund;
use App\Kuafu\Order\Service\OrderService;
use App\Kuafu\Order\Validate\OrderValidate;
use App\Kuafu\Pay\BasePay;
use App\Kuafu\Pay\Exceptions\PayException;
use App\Kuafu\Pay\Model\PayStreamModel;
use App\Kuafu\Pay\Service\PayService;
use App\Kuafu\Shop\Service\ShopPaymentService;
use App\Kuafu\Shop\Service\ShopService;
use App\Kuafu\User\Exceptions\UserException;
use App\Kuafu\WaiMai\Enum\WaiMaiDBEnum;
use App\Kuafu\WaiMai\Service\WaiMaiFinishFoodService;
use App\Kuafu\WaiMai\Service\WaiMaiOrderService;
use App\Kuafu\WaiMai\Service\WaiMaiShopService;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class OrderController extends BaseController
{
    /**
     * 根据id获取详情
     * @param Request $request
     * @param $orderId
     * @return JsonResponse
     */
    public function getOrderById(Request $request, $orderId): JsonResponse
    {
        $orderId = (int)$orderId;
        $valid   = OrderValidate::getOrderById([
            'orderId' => $orderId,
            'shopId'  => $this->shopId,
            'brandId' => $this->brandId
        ]);
        if ($valid) {
            return $this->fail(OrderCodeEnum::ERR_ORDER_NOT_EXIST, $valid);
        }
        $info = OrderService::getOrderByIdAfterChange($this->shopId, $orderId);
        return $this->success($info);
    }

    public function getOrderByIdNew(Request $request, $orderId): JsonResponse
    {
        $orderId = (int)$orderId;
        $valid   = OrderValidate::getOrderById([
            'orderId' => $orderId,
            'shopId'  => $this->shopId,
            'brandId' => $this->brandId
        ]);
        if ($valid) {
            return $this->fail(OrderCodeEnum::ERR_ORDER_NOT_EXIST, $valid);
        }
        $info = OrderService::getOrderByIdAfterChangeNew($this->shopId, $orderId);
        return $this->success($info);
    }

    /**
     * 获取品牌店铺的订单列表
     * @param Request $request
     * @return JsonResponse
     */
    public function getOrderList(Request $request)
    {
        $orderStatus       = $request->orderStatus ?: 0;
        $pageSize          = (int)$request->pageSize ?: 10;
        $pageNum           = (int)$request->pageNum ?: 1;
        $completeTimeStart = $request->completeTimeStart;
        $completeTimeEnd   = $request->completeTimeEnd;
        $serialNum         = ltrim($request->serialNum, '0');
        $orderColumn       = $request->orderColumn;
        $orderType         = (int)$request->orderType ?: 1;
        $orderType         = $orderType == 1 ? "ASC" : "DESC";
        $brandId           = $this->brandId;
        $shopId            = $this->shopId;
        if ($completeTimeEnd <= $completeTimeStart) {
            return $this->fail(OrderCodeEnum::ERR_ORDER_TIME, OrderCodeEnum::CODE_TEXT[OrderCodeEnum::ERR_ORDER_TIME]);
        }
        $field = [
            'serialNum',
            'orderId',
            'originalPrice',
            'shouldPrice',
            'payPrice',
            'discountPrice',
            'payChannel',
            'completeTime',
            'refundStatus',
            'orderStatus',
            'createdBy',
            'refundMoney',
            'createdAt',
            'realPayPrice',
            'realDiscountPrice',
            'invoiceStatus',
            'invoiceCanCancelCount',
            'sourceType'
        ];
        $lists = OrderService::getOrderList($completeTimeStart, $completeTimeEnd, $serialNum, $pageSize, $pageNum,
            $field, $orderColumn, $orderType, $brandId, $shopId, $orderStatus);
        return $this->success($lists);
    }

    /**
     * 订单预计算
     *
     * @param Request $request
     * @return JsonResponse
     * @throws OrderException
     */
    public function preCalc(Request $request): JsonResponse
    {
        $data               = $request->all();
        $data['sourceType'] = CodeEnum::SOURCE_TYPE_POS;
        $postData           = OrderService::getOrderFormatPostData($data);

        $order = new OrderHandleService($this->brandId, $this->shopId, $data['orderId'] ?? '');

        $price = $order->calcPosPrice($postData);

        return $this->success(
            [
                'order' => $price['price'],
            ]
        );
    }

    /**
     * 下单并支付 V5 版本
     * @param $request
     * @return JsonResponse
     */
    public function order(Request $request): JsonResponse
    {
        $data               = $request->all();
        $data['sourceType'] = CodeEnum::SOURCE_TYPE_POS;
        $postData           = OrderService::getOrderFormatPostData($data);

        Log::channel('order')->debug('orderController_order_start_log', [
            'postData' => $postData
        ]);

        try {
            OrderLockService::lockOrderScan($this->shopId, $postData['orderId']);

            $order = new OrderHandleService($this->brandId, $this->shopId, $postData['orderId']);

            /**
             * 判断订单是否是待支付
             */
            OrderException::checkOrderStatus($order);

            /**
             * POS 计价
             */
            $order->calcPosPrice($postData);

            /**
             * 处理订单 应付 金额问题
             */
            $order->getOrderCalc()->calcPosRefundPrice();

            /**
             * 校验金额计算是否相同
             */
            $order->getOrderCalc()->checkPosPrice();

            /**
             * 处理订单 下单 / 更新
             */
            $order->handleOrder($postData);

            /**
             * 处理支付
             */
            $result = $order->posPay($postData);

            /**
             * 统一 数据返回
             */

            $returnResult = OrderService::payResult($result, $order->getOrderPay()->getOrderInfo()->toArray(), $result,
                $order, true);
        } catch (OrderWaringException $orderWaringException) {
            Log::channel('order')->warning('orderController_order_orderWaringException', [
                'postData' => $data,
                'errMsg'   => $orderWaringException
            ]);
            $returnResult = OrderService::payResult([], [], $orderWaringException, $order ?? null);

        } catch (OrderException $orderException) {

            Log::channel('order')->error('orderController_order_orderException', [
                'postData' => $data,
                'errMsg'   => $orderException
            ]);
            $returnResult = OrderService::payResult([], [], $orderException, $order);
        }

        OrderLockService::releaseOrderScan($this->shopId, $postData['orderId']);

        return $this->success($returnResult);

    }

    /**
     * 付款码 支付
     *
     * @param Request $request
     * @return JsonResponse
     * @throws OrderException|PayException
     * @throws OrderWaringException
     */
    public function barPay(Request $request): JsonResponse
    {
        try {
            $orderId     = $request->post('orderId');
            $payAuthCode = $request->post('payAuthCode', '');

            OrderLockService::lockOrderScan($this->shopId, $orderId, OrderLockService::LOCK_METHOD_TYPE_BAR_PAY);

            BasePay::checkAuthCode($payAuthCode);

            // 支付订单
            $orderPay = new OrderPay($this->brandId, $this->shopId, $orderId);

            /**
             * 判断订单是否是待支付
             */
            OrderException::checkOrderStatus($orderPay);

            // 订单上锁
            $orderPay->lockPay('barPay');

            Log::channel('order')->info('orderController_barPay', [
                'postData'  => $request->all(),
                'orderInfo' => $orderPay->getOrderInfo()
            ]);

            $orderPay->setPayData([
                'payMethod'   => CodeEnum::ORDER_PAY_CHANNEL_WECHAT,
                'payAuthCode' => $payAuthCode
            ]);
            $result = $orderPay->barPay();

            OrderLockService::releaseOrderScan($this->shopId, $orderId, OrderLockService::LOCK_METHOD_TYPE_BAR_PAY);

            if (!empty($result['msg'])) {
                $order = new OrderHandleService($this->brandId, $this->shopId, $orderId);
                return $this->success(OrderService::payResult($result, $orderPay->getOrderInfo()->toArray(), $result,
                    $order));
            } else {
                return $this->success(OrderService::payResult($result, $orderPay->getOrderInfo()->toArray(), $result));
            }
        } catch (\Exception $exception) {
            OrderLockService::releaseOrderScan($this->shopId, $orderId, OrderLockService::LOCK_METHOD_TYPE_BAR_PAY);
            throw $exception;
        }
    }

    /**
     * 现金-结账
     * @param Request $request
     * @return JsonResponse
     * @throws OrderException
     * @deprecated  不用了
     */
    public function cashPay(Request $request)
    {
        if (OrderService::checkOrderIsExist($this->shopId, $request->orderId)) {
            return $this->fail(OrderCodeEnum::ERR_ORDER_ID_NOT_EXIST,
                OrderCodeEnum::CODE_TEXT[OrderCodeEnum::ERR_ORDER_ID_NOT_EXIST]);
        }
        $orderPay = new OrderPay($this->brandId, $this->shopId, $request->post('orderId'));

        $orderInfo = $orderPay->getOrderInfo();
        // 判断订单是否是待支付
        if (!empty($orderInfo) && !in_array($orderInfo->payStatus, [
                CodeEnum::ORDER_PAY_STATUS_WAIT,
                CodeEnum::ORDER_PAY_STATUS_REVERSE,
            ])
        ) {

            $payStatus = CodeEnum::ORDER_PAY_STATUSES[$orderInfo->payStatus] ?? '';
            throw new OrderException(OrderCodeEnum::ERR_ORDER_DB_ADD,
                '订单支付状态为:' . $payStatus . ', 不能下单支付');
        }
        // 订单上锁
        $orderPay->lockPay('cashPay');
        $orderPay->setPayData([
            'payMethod' => CodeEnum::ORDER_PAY_CHANNEL_MONEY,
            'payEvent'  => CodeEnum::ORDER_PAY_EVENT_ORDER
        ]);
        $result = $orderPay->cashPay();
        $orderPay->successV2();
        $orderPay->finish();

        return $this->success(OrderService::payResult($result, $orderPay->getOrderInfo()->toArray()));
    }

    /**
     * 根据id获取详情
     *
     * @param Request $request
     * @param $orderId
     * @return JsonResponse
     */
    public function getPayStatus(Request $request, $orderId): JsonResponse
    {
        $orderId = (int)$orderId;
        $valid   = OrderValidate::getOrderById([
            'orderId' => $orderId,
            'shopId'  => $this->shopId,
            'brandId' => $this->brandId
        ]);
        if ($valid) {
            return $this->fail(OrderCodeEnum::ERR_ORDER_NOT_EXIST, $valid);
        }

        $order     = new OrderHandleService($this->brandId, $this->shopId, $orderId);
        $orderInfo = $order->getOrderInfo();

        $status = $orderInfo->payStatus;

        $ttl = $order->getLockTime(CodeEnum::ORDER_LOCK_TYPE_ORDER_PAY);

        if ($ttl > 0) {
            $status = CodeEnum::ORDER_PAY_STATUS_PAYING;
        }

        $result = [
            'order' => [
                'payStatus' => $status
            ]
        ];

        Log::channel('order')->info('orderController_getPayStatus', [
            'orderId' => $orderId,
            'result'  => $result
        ]);

        return $this->success($result);
    }

    /**
     * 主动刷新订单支付状态
     *
     * @param Request $request
     * @param string $orderId
     * @return JsonResponse
     * @throws OrderException
     * @throws PayException
     * @throws CustomerCouponException
     */
    public function refreshPayStatus(Request $request, string $orderId)
    {
        $valid = OrderValidate::getOrderById([
            'orderId' => $orderId,
            'shopId'  => $this->shopId,
            'brandId' => $this->brandId
        ]);
        if ($valid) {
            return $this->fail(OrderCodeEnum::ERR_ORDER_NOT_EXIST, $valid);
        }

        $data = PayService::sRefreshQuery($orderId, $this->shopId, PayStreamModel::SYS_TYPE_RMS_MEAL);
        return $this->success($data);
    }

    /**
     * 核销/撤销优惠券V2
     *
     * @return JsonResponse
     * @throws OrderException
     */
    public function checkCouponV2(Request $request)
    {
        $data               = $request->all();
        $data['sourceType'] = CodeEnum::SOURCE_TYPE_POS;
        $postData           = OrderService::getOrderFormatPostData($data);

        try {
            $order = new OrderHandleService($this->brandId, $this->shopId, $postData['orderId'] ?? '');

            // 小程序的订单 POS上不能核销三方优惠券操作
            if (($order->getOrderInfo()['sourceType'] ?? 0) == CodeEnum::SOURCE_TYPE_MINI) {
                throw new OrderException(OrderCodeEnum::ERR_ORDER_DB_ADD, '小程序下的订单不能核销三方优惠券。');
            }

            // 判断 三方券类型,不可叠加使用
            $extCouponCheckList = array_column($postData['extCoupon'], 'type');
            $extCouponCheckList = array_unique($extCouponCheckList);
            // 判断是否大于两种优惠券
            if (count($extCouponCheckList) >= 2) {
                throw new OrderException(OrderCodeEnum::ERR_ORDER_DB_ADD, '美团和抖音的优惠券不能同时使用。');
            }

            // 判断是否能退券
            if (!empty($postData['refundCoupon'])) {
                self::isCanRefundCouponExt($order);
                $postData['orderEvent'] = CodeEnum::ORDER_PAY_EVENT_REFUND;
            }

            // 计价
            $order->calcPosPrice($postData);
            // 校验金额是否相同up
            $order->getOrderCalc()->checkPosPrice();

            $order->handleOrder($postData);

            // 核销三方优惠券
            if (!empty($order->getOrderCalc()->getNeedCheckExtCoupon())) {
                // 核销优惠券
                foreach ($order->getOrderCalc()->getNeedCheckExtCoupon() as $coupon) {
                    $couponType = $coupon['type'] ?? OrderInfoEnum::POS_ORDER_PAYMENT_COUPON_MEITUAN;
                    $couponExt  = new OrderCouponExternalService(0, $this->shopId, $this->brandId,
                        $order->getOrderId(true));
                    $couponExt->resetCouponExtPlatformByPosPayment($couponType);
                    $couponExt->consumeCouponExt($coupon['code'], $coupon['num'], (int)$coupon['couponDiscountPrice'],
                        $coupon['customData'] ?? []);
                }
            }

            // 撤销三方优惠券
            if (!empty($order->getOrderCalc()->getNeedRefundExtCoupon())) {
                // 核销优惠券
                foreach ($order->getOrderCalc()->getNeedRefundExtCoupon() as $coupon) {
                    $couponType = $coupon['type'] ?? OrderInfoEnum::POS_ORDER_PAYMENT_COUPON_MEITUAN;
                    $couponExt  = new OrderCouponExternalService(0, $this->shopId, $this->brandId,
                        $order->getOrderId(true));
                    $couponExt->resetCouponExtPlatformByPosPayment($couponType);
                    $couponExt->cancelCouponByOrder($coupon['code']);

                    // 判断是否是通过券码核销进来的
                    if ($order->getOrderInfo()->sourceType == CodeEnum::SOURCE_TYPE_MERCHANT_CHECK_COUPON) {
                        // 撤销原来的订单数据情况
                        CustomerCouponService::cancelExtUsedCoupon($coupon['code'], $this->shopId);
                    }
                }

            }

            $orderResult                  = $order->getOrderInfo(true)->toArray();
            $orderResult['extCouponList'] = $order->getOrderExtCoupon(true);

            return $this->success(OrderService::payResult([], $orderResult, [], $order));

        } catch (OrderException $orderException) {

            Log::channel('order')->error('orderController_orderError_checkCouponV2_1', [
                'postData' => $data,
                'errMsg'   => $orderException->getMessage()
            ]);
            return $this->success(OrderService::payResult([], [], $orderException, $order));
        } catch (CouponExtException $couponExtException) {

            Log::channel('order')->error('orderController_orderError_checkCouponV2_2', [
                'postData' => $data,
                'errMsg'   => $couponExtException->getMessage()
            ]);
            return $this->success(OrderService::payResult([], [], $couponExtException, $order));
        } catch (\Exception $exception) {
            Log::channel('order')->error('orderController_orderError_checkCouponV2_3', [
                'postData' => $data,
                'errMsg'   => $exception->getMessage(),
                'line'     => $exception->getLine(),
                'file'     => $exception->getFile(),
            ]);
            return $this->success(OrderService::payResult([], [], $exception, $order));
        }
    }

    public function isCanRefundCouponExt($order)
    {
        /**
         * 订单存在抖音优惠券且超过 1 小时无法 反结账
         */
        $douyinCoupon = OrderCouponExtSplitModel::suffix($this->shopId)
                                                ->query()
                                                ->where('orderId', $order->orderId)
                                                ->where('extPlatform', CouponExtEnum::EXT_PLATFORM_DOUYIN)
                                                ->where('couponExtStatus', CouponExtEnum::COUPON_STATUS_USED)
                                                ->orderBy('createdAt')
                                                ->first();

        if (!empty($douyinCoupon) && time() - $order->getOrderInfo()->createdAt->timestamp > 3500) {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_CANNOT_RESERVE,
                '抖音券已核销超过1小时，无法反结账。');
        }
    }

    /**
     * 反结账 V6
     * @param Request $request
     * @return JsonResponse
     * @throws GuzzleException
     * @throws OrderException
     * @throws OrderWaringException
     * @throws UserException
     */
    public function antiSettlementV6(Request $request): JsonResponse
    {
        $data                   = $request->all();
        $data['sourceType']     = CodeEnum::SOURCE_TYPE_POS;
        $postData               = OrderService::getOrderFormatPostData($data);
        $postData['orderEvent'] = CodeEnum::ORDER_PAY_EVENT_REFUND;

        OrderLockService::lockOrderScan($this->shopId, $postData['orderId']);

        Log::channel('order')->debug('orderController_antiSettlementV6_start_log', [
            'postData' => $postData
        ]);

        $order = new OrderHandleService($this->brandId, $this->shopId, $postData['orderId']);

        // 判断订单是否存在
        if (empty($order->getOrderInfo())) {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_ID_NOT_EXIST);
        }
        $lock = $order->lockOrder(5);

        // 订单已经上锁了
        if (!$lock) {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_HAS_BAR_PAYING, '该笔订单正在反结账，请稍后再试。');
        }

        /**
         * 订单存在券包订单不允许反结账
         */
        $couponPackageOrder = OrderExternalSplitModel::suffix($this->shopId)
                                                     ->query()
                                                     ->where('orderId', $order->getOrderId(true))
                                                     ->where('extOrderId', '<>', 0)
                                                     ->where('type', CodeEnum::ORDER_EXTERNAL_TYPE_COUPON_PACKAGE)
                                                     ->first();
        if (!empty($couponPackageOrder)) {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_CANNOT_RESERVE, '该订单含有券包订单，不能反结账。');
        }

        try {
            // 计价
            $order->calcPosPrice($postData);
            // 校验金额计算是否相同
            $order->getOrderCalc()->checkPosPrice();

            $refundPrice = $order->getOrderCalc()->calcPosRefundPrice();

            $order->updateOrder($postData);

        } catch (OrderWaringException $orderWaringException) {
            Log::channel('order')->warning('orderController_orderError_antiSettlementV6_1', [
                'postData' => $data,
                'errMsg'   => $orderWaringException
            ]);

            return $this->success(OrderService::payResult([], [], $orderWaringException, $order));
        } catch (OrderException $orderException) {
            Log::channel('order')->error('orderController_orderError_antiSettlementV6_2', [
                'postData' => $data,
                'errMsg'   => $orderException
            ]);
            return $this->success(OrderService::payResult([], [], $orderException, $order));
        }

        // 退款
        try {
            $refund = new OrderRefund($this->brandId, $this->shopId, $data['orderId']);
            $refund->updateOrderRefundV2();
        } catch (\Exception $exception) {
            Log::debug('订单反结账失败, 请管理员过来看下', [$exception->getMessage()]);
            Log::channel('order')->debug('order_AntiSettlementV6', [
                'postData' => $postData,
                'orgData'  => $order->getOrderInfo()
            ]);
            throw new OrderException(OrderCodeEnum::ERR_ORDER_ADD_ERROR, '订单反结账失败', $exception);
        }

        // 仅作为退款
        $payResult = [];
        // 补钱
        if (!empty($postData['payMethod'])) {
            $orderShouldPrice = $order->getOrderCalc()->getOrderShouldPrice();

            if (
                ($refundPrice > 0 && $orderShouldPrice == 0)
                ||
                ($refundPrice == 0 && $orderShouldPrice >= 0)
                ||
                (bcadd($orderShouldPrice, $refundPrice) == 0)
            ) {
                // 支付订单
                $orderPay = new OrderPay($this->brandId, $this->shopId, $postData['orderId']);
                try {
                    // 更新订单为反结账状态
                    switch ($data['payMethod'] ?? 0) {
                        case CodeEnum::ORDER_PAY_CHANNEL_ALIPAY:            // 支付宝
                        case CodeEnum::ORDER_PAY_CHANNEL_WECHAT:
                            $payAuthCode = $data['payAuthCode'] ?? '';
                            $orderPay->updateOrderStatus(CodeEnum::ORDER_STATUS_REVERSE);
                            $orderPay->setPayData([
                                'payEvent'    => CodeEnum::ORDER_PAY_EVENT_REFUND,
                                'payMethod'   => CodeEnum::ORDER_PAY_CHANNEL_WECHAT,
                                'payAuthCode' => $payAuthCode,
                            ]);
                            $payResult = $orderPay->barPay();
                            break;
                        case CodeEnum::ORDER_PAY_CHANNEL_MONEY:            // 现金支付
                            $orderPay->setPayData([
                                'payEvent'  => CodeEnum::ORDER_PAY_EVENT_REFUND,
                                'payMethod' => CodeEnum::ORDER_PAY_CHANNEL_MONEY,
                            ]);
                            $payResult = $orderPay->cashPay();
                            $orderPay->successV2();
                            $orderPay->finish();
                            break;
                        default:
                            throw new OrderException(OrderCodeEnum::ERR_ORDER_PAY_METHOD_ERR);
                    }
                } catch (OrderException $exception) {
//                    $orderPay->updateOrderStatus(CodeEnum::ORDER_STATUS_FINISHED);
                    Log::channel('order')->error('orderController_OrderException_addOrderAndPayV4', [
                        'orderId'  => $postData['orderId'],
                        'postData' => $data,
                        'errMsg'   => $exception
                    ]);
                } catch (PayException $exception) {
                    $payResult = [
                        'code' => $exception->code ?? 500,
                        'msg'  => $exception->message ?? '服务器开小差了,请稍后再试'
                    ];
                    Log::channel('order')->error('orderController_PayException_addOrderAndPayV4', [
                        'orderId'  => $postData['orderId'],
                        'postData' => $data,
                        'errMsg'   => $exception
                    ]);
                }
            }
        }
        // 解锁
        $order->unLockOrder();

        // 判断是否需要取消发票

        if ($order->orderInfo->invoiceStatus == CodeEnum::ORDER_INVOICE_STATUS_WAIT ||
            $order->orderInfo->invoiceStatus == CodeEnum::ORDER_INVOICE_STATUS_HAVEN) {
            try {
                $invoiceResult = InvoiceService::redInvoiceOrder($order->orderInfo->shopId,
                    $order->orderInfo->orderId,
                    CodeEnum::ORDER_OUT_INVOICE_SOURCE_REVERT);

                if ($invoiceResult['recallStatus']) {
                    OrderInvoiceService::InvoiceStatusUpdate($order->orderInfo->orderId,
                        $order->orderInfo->shopId, CodeEnum::SOURCE_TYPE_SHOP, CodeEnum::ORDER_INVOICE_STATUS_CANCELED);
                }

            } catch (\Exception $exception) {
                Log::channel('order')->error('orderController_orderError_addOrderAndPayV4_redInvoiceOrder', [
                    'orderId'  => $postData['orderId'],
                    'postData' => $data,
                    'errMsg'   => $exception
                ]);
            }
        }
        OrderLockService::releaseOrderScan($this->shopId, $postData['orderId']);

        return $this->success(OrderService::payResult($payResult, $order->orderInfo->toArray(), $payResult, $order));
    }

    /**
     * 店铺结算（日结单）
     * @param Request $request
     * @return JsonResponse
     * @throws OrderException
     */
    public function calcShopTrade(Request $request): JsonResponse
    {
        $startTime = $request->get('startDate', strtotime(date('Ymd')));
        $endTime   = $request->get('endDate', strtotime(date('Ymd')));

        $shopId = (int)$request->header('shopId');

        if ($endTime - $startTime > 60 * 60 * 24 * 31) {
            throw new OrderException(OrderCodeEnum::ERR_QUERY_TIME);
        }

        // 加锁
        $lockKey = OrderCacheEnum::getShopTradeLock($shopId);

        if (!Redis::setnx($lockKey, time())) {

            // 防止死锁
            $lock_time = Redis::get($lockKey);
            if (($lock_time + 5) < time()) {
                Redis::del($lockKey);
            } else {
                return $this->fail(OrderCodeEnum::ERR_ORDER_REQUEST_BUSY);
            }
        }

        try {
            $data = OrderService::sCalcShopTrade($shopId, $startTime, $endTime);
        } catch (\Exception $e) {
            Redis::del($lockKey);
            throw new OrderException(OrderCodeEnum::ERR_ORDER_STATIC, '', $e);
        }

        return $this->success($data);
    }

    /**
     * POS 订单离线同步
     *
     * @param Request $request
     * @return JsonResponse
     */
    public function syncOrder(Request $request): JsonResponse
    {
        $orderData = $request->post('orderData');

        $orderHandle = new OrderHandleService($this->brandId, $this->shopId);
        $successList = [];
        $failList    = [];


        if (!is_array($orderData)) {
            return $this->success([
                'msg'     => '提交数据有误',
                'success' => $successList,
                'fail'    => $failList
            ]);
        }
        foreach ($orderData as $orderDetail) {
            try {
                // 校验订单 Sign 是否合规
                if (!OrderService::checkOrderSign($orderDetail)) {
                    $failList[] = [
                        'orderId' => $orderDetail['orderId'],
                        'msg'     => OrderCodeEnum::CODE_TEXT[OrderCodeEnum::ERR_ORDER_OFFLINE_SIGN_ERROR],
                        'code'    => OrderCodeEnum::ERR_ORDER_OFFLINE_SIGN_ERROR
                    ];
                    continue;
                }
                // 校验 OrderId 是否存在
                if (!OrderService::checkOffOrderIsExist($this->shopId, $orderDetail['orderId'])) {
                    $failList[] = [
                        'orderId' => $orderDetail['orderId'],
                        'msg'     => OrderCodeEnum::CODE_TEXT[OrderCodeEnum::ERR_ORDER_OFFLINE_ORDER_EXIST],
                        'code'    => OrderCodeEnum::ERR_ORDER_OFFLINE_ORDER_EXIST
                    ];
                    continue;
                }
                // 初步校验数据是否正确
                if (!OrderOfflineService::checkDataInputData($orderDetail)) {
                    $failList[] = [
                        'orderId' => $orderDetail['orderId'],
                        'msg'     => OrderCodeEnum::CODE_TEXT[OrderCodeEnum::ERR_ORDER_OFFLINE_PRICE_ERROR],
                        'code'    => OrderCodeEnum::ERR_ORDER_OFFLINE_PRICE_ERROR
                    ];
                    continue;
                }

                $orderDetail['brandId']    = $this->brandId;
                $orderDetail['shopId']     = $this->shopId;
                $orderDetail['sourceType'] = CodeEnum::SOURCE_TYPE_POS;
                $orderHandle->createOrderOffline($orderDetail);

                $successList[] = $orderDetail['orderId'];

                // 判断是否前天以前的订单
                if (strtotime(date('Y-m-d')) - $orderDetail['completeTime'] > 86400 || isTest()) {
                    // 数据同步更新操作

                    $nsqService = new NsqService();
                    $nsqService->setTopic(NsqCodeEnum::ORDER_NSQ_UPDATE_SEND_QUEUE);
                    $nsqService->setBody([
                        'shopId'    => $this->shopId,
                        'orderId'   => $orderDetail['orderId'],
                        'orderType' => CodeEnum::SOURCE_TYPE_SHOP,
                        'updatedAt' => $orderDetail['completeTime']
                    ]);
                    $nsqService->sendNsq();
                }
            } catch (\Exception $exception) {
                $failList[] = [
                    'orderId' => $orderDetail['orderId'],
                    'msg'     => $exception->getMessage()
                ];
            }
        }

        return $this->success([
            'msg'     => '',
            'success' => $successList,
            'fail'    => $failList
        ]);
    }

    /**
     * 外卖订单列表
     * @param Request $request
     * @return JsonResponse
     */
    public function waiMaiOrderLists(Request $request): JsonResponse
    {
        $sourceType        = $request->get('sourceType');
        $orderStatus       = $request->orderStatus ?? WaiMaiDBEnum::ORDER_STATUS_TOTAL;
        $pageSize          = (int)$request->pageSize ?: 10;
        $pageNum           = (int)$request->pageNum ?: 1;
        $completeTimeStart = $request->completeTimeStart;
        $completeTimeEnd   = $request->completeTimeEnd;
        $serialNum         = (int)ltrim($request->serialNum, '0');
        $orderColumn       = (string)$request->orderColumn;
        $orderType         = (int)$request->orderType ?: 1;
        $orderType         = $orderType == 1 ? "ASC" : "DESC";
        $shopId            = (int)$request->header('shopId');
        if ($completeTimeStart && $completeTimeEnd && ($completeTimeEnd <= $completeTimeStart)) {
            return $this->fail(OrderCodeEnum::ERR_ORDER_TIME, OrderCodeEnum::CODE_TEXT[OrderCodeEnum::ERR_ORDER_TIME]);
        }

        return $this->success(WaiMaiOrderService::orderList($shopId, $completeTimeStart, $completeTimeEnd, $serialNum,
            $orderStatus, $sourceType, $orderColumn, $orderType, $pageSize, $pageNum));
    }

    /**
     * 外卖订单详情
     * @param Request $request
     * @return JsonResponse
     */
    public function waiMaiOrderDetail(Request $request)
    {
        $orderId = (int)$request->get('orderId');

        return $this->success(WaiMaiOrderService::orderDetail($orderId));
    }

    /**
     * 返回支付方式
     *
     * @return JsonResponse
     */
    public function getPaymentList(): JsonResponse
    {
        $commonContent = [
            'type'   => 0,
            'name'   => '',
            'image'  => '',
            'enable' => 1,
            'date'   => null
        ];

        $paymentList = [];
        foreach (CodePaymentEnum::ORDER_PAYMENT_LIST as $payment) {
            if (in_array($payment, [
                CodePaymentEnum::ORDER_PAYMENT_TYPE_COUPON,
                CodePaymentEnum::ORDER_PAYMENT_TYPE_CUSTOM,
            ])) {
                continue;
            }

            $paymentList[] = array_merge($commonContent, [
                'type'  => $payment,
                'name'  => CodePaymentEnum::ORDER_PAYMENT_NAME[$payment],
                'image' => CommonService::joinImageUrl(CodePaymentEnum::ORDER_PAYMENT_IMAGE[$payment]['enable']),
            ]);
        }

        $paymentList = array_column($paymentList, null, 'type');


        // 判断是否开通对应的权限
        $mtConfig = WaiMaiShopService::getMappingCache($this->shopId, WaiMaiDBEnum::SOURCE_TYPE_MT_TG);
        if (empty($mtConfig)) {
            unset($paymentList[CodePaymentEnum::ORDER_PAYMENT_TYPE_MEITUAN]);
        }

        // 抖音
        $dyConfig = WaiMaiShopService::getMappingCache($this->shopId, WaiMaiDBEnum::SOURCE_TYPE_DOUYIN);
        if (empty($dyConfig)) {
            unset($paymentList[CodePaymentEnum::ORDER_PAYMENT_TYPE_DOUYIN]);
        }

        // 小红书核销券码
        $xhsConfig = WaiMaiShopService::getMappingCache($this->shopId, WaiMaiDBEnum::SOURCE_TYPE_XHS);
        if (empty($xhsConfig)) {
            unset($paymentList[CodePaymentEnum::ORDER_PAYMENT_TYPE_COUPON_XHS]);
        }

        $paymentList = array_values($paymentList);

        // 获取优惠券列表
        $posEnableCouponList = CouponService::getPosEnableCouponList($this->brandId, $this->shopId, [], [],
            CodeEnum::SOURCE_TYPE_POS);
        $enableList          = $posEnableCouponList->getCanUseCouponList();
        foreach ($enableList as $enable) {
            if ($enable['type'] == CouponEnum::COUPON_TYPE_EXCHANGE) {
                continue;
            }

            $paymentList[] = array_merge($commonContent, [
                'type'  => CodePaymentEnum::ORDER_PAYMENT_TYPE_COUPON,
                'name'  => $enable['name'],
                'image' => CommonService::joinImageUrl(CodePaymentEnum::ORDER_PAYMENT_IMAGE[CodePaymentEnum::ORDER_PAYMENT_TYPE_COUPON]['enable']),
                'date'  => [
                    'couponId'     => (string)$enable['couponId'],
                    'costPrice'    => $enable['costPrice'],
                    'useMax'       => $enable['useMax'],
                    'couponType'   => $enable['type'],
                    'useCondition' => $enable['useCondition'],
                    'discount'     => $enable['discount']
                ]
            ]);
        }

        //获取自定义支付方式
        $customPaymentLists = CustomPaymentService::enableCustomPaymentLists($this->shopId)['customPaymentLists'];
        if (!empty($customPaymentLists)) {
            (new Collection($customPaymentLists))->each(function ($v) use (&$paymentList, $commonContent) {
                if (isset($v['isShow']) && $v['isShow'] === 0) {
                    return;
                }
                array_push($paymentList, array_merge($commonContent, [
                    'type'  => CodePaymentEnum::ORDER_PAYMENT_TYPE_CUSTOM,
                    'name'  => $v['name'] ?? '',
                    'image' => CommonService::joinImageUrl(CodePaymentEnum::ORDER_PAYMENT_IMAGE[CodePaymentEnum::ORDER_PAYMENT_TYPE_CUSTOM]['enable']),
                    'date'  => [
                        'id' => $v['payId']
                    ]
                ]));
            });
        }

        return $this->success($paymentList);
    }

    public function getPaymentListAll(): JsonResponse
    {
        $paymentList = $this->getPaymentList();
        $paymentList = $paymentList->getOriginalContent();
        $paymentList = $paymentList['data'];

        // 历史支付方式
        $result['paymentList'] = $paymentList;
        $shopPayment           = new ShopPaymentService($this->brandId, $this->shopId);
        // 富友支付方式
        $result['fyPay'] = $shopPayment->getFyPayConfig();

        // todo 获取满赠活动信息
        $result["giftActivity"] = ActivityService::getShopGiftActivityDetail(
            ShopService::getShopInfo($this->shopId),
            ActivityEnum::CHANNEL_POS
        );

        return $this->success($result);

    }

    /**
     * 商家出餐
     *
     * @param Request $request
     * @param $orderId
     * @return JsonResponse
     * @throws GuzzleException
     * @throws \App\Kuafu\WaiMai\Exceptions\WaiMaiException
     */
    public function finishFoods(Request $request, $orderId)
    {
        WaiMaiOrderService::sFinishFoods((int)$orderId, $this->shopId);

        return $this->success();
    }

    /**
     * 商家确认退款
     *
     * @param Request $request
     * @param $orderId
     * @return JsonResponse
     * @throws GuzzleException
     * @throws \App\Kuafu\WaiMai\Exceptions\WaiMaiException
     */
    public function confirmRefund(Request $request, $orderId)
    {
        $agree  = (bool)$request->agree ?? 0;
        $reason = (string)$request->reason ?? '';

        WaiMaiOrderService::sConfirmRefund($orderId, $this->shopId, $reason, $agree);

        return $this->success();
    }

    /**
     * 获取店铺出餐配置信息
     * @param Request $request
     */
    public function getFinishFoodConfig(Request $request)
    {
        $shopId = $request->header('shopId');

        return $this->success((new WaiMaiFinishFoodService($shopId))->getConfig());
    }

    /**
     * 设置店铺出餐配置信息
     * @param Request $request
     */
    public function setFinishFoodConfig(Request $request)
    {
        $shopId         = $request->header('shopId');
        $switch         = $request->get('switch');
        $lists          = $request->get('lists');
        $reservePrepare = $request->get('reservePrepare');
        $reserveFinish  = $request->get('reserveFinish');

        (new WaiMaiFinishFoodService($shopId))->setConfig($switch, $lists, $reservePrepare, $reserveFinish);

        return $this->success();
    }

    /**
     * 自动出餐挂起
     * @param Request $request
     */
    public function delayFinishFoodsConfig(Request $request)
    {
        $shopId  = $request->header('shopId');
        $orderId = $request->get('orderId');
        $seconds = $request->get('seconds');

        (new WaiMaiFinishFoodService($shopId))->delayPush($orderId, $seconds);

        return $this->success();
    }

    /**
     * 获取承诺出餐配置
     * @param Request $request
     */
    public function getPromiseFinishConfig(Request $request)
    {
        $shopId = $request->header('shopId');

        return $this->success((new WaiMaiFinishFoodService($shopId))->getPromiseFinishConfig());
    }

    /**
     * 设置承诺出餐时间
     * @param Request $request
     * @return JsonResponse
     */
    public function setPromiseFinishConfig(Request $request)
    {

        $this->validate($request, [
            'kfPromiseTime'    => 'required|integer|min:1',
            'elmPromiseSwitch' => 'required|integer|between:0,1',
            'silentSwitch'     => 'required|integer|between:0,1',
        ]);

        $shopId           = $request->header('shopId');
        $kfPromiseTime    = $request->get('kfPromiseTime');
        $elmPromiseSwitch = $request->get('elmPromiseSwitch');
        $silentSwitch     = $request->get('silentSwitch');

        (new WaiMaiFinishFoodService($shopId))->setPromiseFinishConfig($kfPromiseTime, $elmPromiseSwitch,
            $silentSwitch);

        return $this->success();
    }
}
